/********************************************************************
	created:	2008/10/17
	created:	17:10:2008   0:15
	filename: 	h:\SimoneEngine\qianye\src\qylib\math\qyOBB.h
	author:		Xiong Jiayu
	copyright:  
	purpose:	
*********************************************************************/
#pragma once

#ifndef __QYOBB_H__
#define __QYOBB_H__

#include "../common/qyCommon.h"
#include "qyMatrix3.h"
#include "qyVector3.h"
#include "qyAABB.h"

template<class T> QY_INLINE int	Min3Index( T x, T y, T z ) { return ( x < y ) ? ( ( x < z ) ? 0 : 2 ) : ( ( y < z ) ? 1 : 2 ); }

/*
===========================================
struct OBB
===========================================
*/
struct OBB
{
public:
	Vector3					center;
	Vector3					extents;
	Matrix3					axis;
public:
							OBB( void );
							OBB( const Vector3 &center, const Vector3 &extents, const Matrix3 &axis );
							OBB( const Vector3 &point );
							OBB( const OBB &a );

	OBB						operator+( const Vector3 &t ) const;
	OBB &					operator+=( const Vector3 &t );
	OBB						operator*( const Matrix3 &r ) const;
	OBB &					operator*=( const Matrix3 &r );
	OBB						operator+( const OBB &a ) const;
	OBB &					operator+=( const OBB &a );
	OBB						operator-( const OBB &a ) const;
	OBB	&					operator-=( const OBB &a );

	bool					Compare( const OBB &a ) const;
	bool					Compare( const OBB &a, const float epsilon ) const;
	bool					operator==( const OBB &a ) const;
	bool					operator!=( const OBB &a ) const;

	void					Clear( void );
	void					Zero( void );

	const Vector3 &			GetCenter( void ) const;
	const Vector3 &			GetExtents( void ) const;
	const Matrix3 &			GetAxis( void ) const;
	float					GetVolume( void ) const;
	bool					IsCleared( void ) const;

	bool					AddPoint( const Vector3 &p );
	bool					AddBox( const OBB &a );
	OBB						Expand( const float d ) const;
	OBB &					ExpandSelf( const float d );
	OBB						Translate( const Vector3 &t ) const;
	OBB &					TranslateSelf( const Vector3 &t );
	OBB &					TranslateTo( const Vector3 &pos );
	OBB						Rotate( const Matrix3 &rot ) const;
	OBB	&					RotateSelf( const Matrix3 &rot );
	OBB &					RotateTo( const Matrix3 &rot );

    void                    FromAABB( const AABB &box );
    void                    ToAABB( AABB &box );

	void					AxisProjection( const Vector3 &dir, float &min, float &max ) const;
	void					AxisProjection( const Matrix3 &mat, AABB &bound ) const;

	bool					ContainsPoint( const Vector3 &p ) const;

	bool					IntersectsBox( const OBB &a ) const;
	bool					LineIntersection( const Vector3 &start, const Vector3 &end ) const;
	bool					RayIntersection( const Vector3 &start, const Vector3 &dir, float &scale1, float &scale2 );
};

//-----------------------------------------------------------------------
QY_INLINE OBB::OBB( void )
{
}

//-----------------------------------------------------------------------
QY_INLINE OBB::OBB( const Vector3 &center, const Vector3 &extents, const Matrix3 &axis )
{
	this->center = center;
	this->extents = extents;
	this->axis = axis;
}

//-----------------------------------------------------------------------
QY_INLINE OBB::OBB(const Vector3 &point)
{
	this->center = point;
	this->extents.Zero();
	this->axis.Identity();
}

//-----------------------------------------------------------------------
QY_INLINE OBB::OBB(const OBB &a)
{
	center = a.center;
	extents = a.extents;
	axis = a.axis;
}

//-----------------------------------------------------------------------
QY_INLINE OBB OBB::operator +( const Vector3 &t ) const
{
	return OBB( center + t, extents, axis );
}

//-----------------------------------------------------------------------
QY_INLINE OBB & OBB::operator +=( const Vector3 &t )
{
	center += t;
	return (*this);
}

//-----------------------------------------------------------------------
QY_INLINE OBB OBB::operator *( const Matrix3 &r ) const
{
	return OBB( center * r, extents, axis * r );
}

//-----------------------------------------------------------------------
QY_INLINE OBB & OBB::operator *=( const Matrix3 &r )
{
	center *= r;
	axis *= r;
	return (*this);
}

//-----------------------------------------------------------------------
QY_INLINE OBB OBB::operator +( const OBB &a ) const
{
	OBB newBox;
	newBox = *this;
	newBox.AddBox( a );
	return newBox;
}

//-----------------------------------------------------------------------
QY_INLINE OBB & OBB::operator +=( const OBB &a )
{
	AddBox( a );
	return (*this);
}

//-----------------------------------------------------------------------
QY_INLINE OBB OBB::operator -( const OBB &a ) const
{
	return OBB( center, extents - a.extents, axis );
}

//-----------------------------------------------------------------------
QY_INLINE OBB & OBB::operator -=( const OBB &a )
{
	extents -= a.extents;
	return (*this);
}

//-----------------------------------------------------------------------
QY_INLINE bool OBB::Compare(const OBB &a) const
{
	return ( center.Compare( a.center) && extents.Compare( a.extents ) && axis.Compare( a.axis ) );
}

//-----------------------------------------------------------------------
QY_INLINE bool OBB::Compare(const OBB &a, const float epsilon) const
{
	return ( center.Compare( a.center, epsilon ) && extents.Compare( a.extents, epsilon ) && axis.Compare( a.axis, epsilon ) );
}

//-----------------------------------------------------------------------
QY_INLINE bool OBB::operator ==( const OBB &a ) const
{
	return Compare( a );
}

//-----------------------------------------------------------------------
QY_INLINE bool OBB::operator !=( const OBB &a ) const
{
	return !Compare( a );
}

//-----------------------------------------------------------------------
QY_INLINE void OBB::Clear()
{
	center.Zero();
	extents[0] = extents[1] = extents[2] = -100;
	axis.Identity();
}

//-----------------------------------------------------------------------
QY_INLINE void OBB::Zero()
{
	center.Zero();
	extents.Zero();
	axis.Identity();
}

//-----------------------------------------------------------------------
QY_INLINE const Vector3 & OBB::GetCenter() const
{
	return center;
}

//-----------------------------------------------------------------------
QY_INLINE const Vector3 & OBB::GetExtents() const
{
	return extents;
}

//-----------------------------------------------------------------------
QY_INLINE const Matrix3 & OBB::GetAxis() const
{
	return axis;
}

//-----------------------------------------------------------------------
QY_INLINE float OBB::GetVolume() const
{
	return ( extents * 2.0f ).LengthSq();
}

//-----------------------------------------------------------------------
QY_INLINE bool OBB::IsCleared() const
{
	return extents[0] < 0.0f;
}

//-----------------------------------------------------------------------
QY_INLINE OBB OBB::Expand(const float d) const
{
	return OBB( center, extents + Vector3( d, d, d ), axis );
}

//-----------------------------------------------------------------------
QY_INLINE OBB & OBB::ExpandSelf(const float d)
{
	extents[0] += d;
	extents[1] += d;
	extents[2] += d;
	return (*this);
}

//-----------------------------------------------------------------------
QY_INLINE OBB OBB::Translate(const Vector3 &t) const
{
	return OBB( center + t, extents, axis );
}

//-----------------------------------------------------------------------
QY_INLINE OBB & OBB::TranslateSelf(const Vector3 &t)
{
	center += t;
	return (*this);
}

//-----------------------------------------------------------------------
QY_INLINE OBB & OBB::TranslateTo(const Vector3 &pos)
{
	center = pos;
	return (*this);
}

//-----------------------------------------------------------------------
QY_INLINE OBB OBB::Rotate(const Matrix3 &rot) const
{
	return OBB( center * rot, extents, axis * rot );
}

//-----------------------------------------------------------------------
QY_INLINE OBB & OBB::RotateSelf(const Matrix3 &rot)
{
	center *= rot;
	axis *= rot;
	return (*this);
}

//-----------------------------------------------------------------------
QY_INLINE OBB & OBB::RotateTo(const Matrix3 &rot)
{
	axis = rot;
	return (*this);
}

//-----------------------------------------------------------------------
QY_INLINE bool OBB::ContainsPoint(const Vector3 &p) const
{
	Vector3 lp = p - center;
	if ( fabsf( lp * axis[0] ) > extents[0] ||
		fabsf( lp * axis[1] ) > extents[1] ||
		fabsf( lp * axis[2] ) > extents[2] )
	{
		return false;
	}
	return true;
}

//-----------------------------------------------------------------------
QY_INLINE void OBB::AxisProjection(const Vector3 &dir, float &min, float &max) const
{
	float d1 = dir * center;
	float d2 = fabsf( extents[0] * ( dir * axis[0] ) ) +
		fabsf( extents[1] * ( dir * axis[1] ) ) +
		fabsf( extents[2] * ( dir * axis[2] ) );
	min = d1 - d2;
	max = d1 + d2;
}

//-----------------------------------------------------------------------
QY_INLINE void OBB::AxisProjection( const Matrix3 &ax, AABB &bounds ) const 
{
	for ( int i = 0; i < 3; i++ ) {
		float d1 = ax[i] * center;
		float d2 = fabsf( extents[0] * ( ax[i] * axis[0] ) ) +
			fabsf( extents[1] * ( ax[i] * axis[1] ) ) +
			fabsf( extents[2] * ( ax[i] * axis[2] ) );
		bounds[0][i] = d1 - d2;
		bounds[1][i] = d1 + d2;
	}
}

//-----------------------------------------------------------------------
QY_INLINE void OBB::FromAABB( const AABB &box )
{
    const Vector3 & max = box.GetMax();
    const Vector3 & min = box.GetMin();
    center = max + min;
    center *= 0.5f;
    extents = max;
    axis.Identity();
}

//-----------------------------------------------------------------------
QY_INLINE void OBB::ToAABB( AABB &box )
{
    box.Zero();
    Vector3 vec_0 = extents * axis;
    Vector3 vec_1 = - vec_0;
    box.AddPoint( vec_0 );
    box.AddPoint( vec_1 );
    box.TranslateTo( center );
}

#endif

